# -*- coding: utf-8 -*-
# Project : flacon
# File : track.py
# (c) Alexander Sokolov sokoloff.a@gmail.com
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.


import re
from settings import *
from flaconglobal import *

from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import *

class TrackIndex():
   #****************************************************************
   #
   #****************************************************************
    def __init__(self, str = ""):
        self._value = 0
        self._isNull = True

        s = QString(str)
        if str:
            self._isNull = False
            min = int(s.section(":", 0, 0))
            sec = int(s.section(":", 1, 1))
            frm = int(s.section(":", 2, 2))
            self._value = (min * 60 + sec) * 75 + frm


   #****************************************************************
   #
   #****************************************************************
    def asString(self):
        min = 0
        sec = 0
        frm = 0
        msec = 0

        v = int(self._value)
        min = v / (60*75)
        sec = int(v - min * 60 * 75) / 75
        frm = int(v - (min * 60 + sec) * 75)

        res = QString("%1:%2:%3").arg(min, 2, 10, QChar('0')) \
                                 .arg(sec, 2, 10, QChar('0')) \
                                 .arg(frm, 2, 10, QChar('0'))
        return res;


   #****************************************************************
   #
   #****************************************************************
    def __sub__(self, other):
        res = TrackIndex()
        res._value = self._value - other._value
        return res


   #****************************************************************
   #
   #****************************************************************
    def isNull(self):
        return self._isNull


   #****************************************************************
   #
   #****************************************************************
    def __cmp__(self, other):
        v = 0
        if isinstance(other, TrackIndex):
            v = other._value
        else:
            v = other

        return self._value - v;


   #****************************************************************
   #
   #****************************************************************
    def __str__(self):
        return str(self.asString())



class Track(QObject):
   #****************************************************************
   #
   #****************************************************************
    def __init__(self, trackSet):
        QObject.__init__(self, trackSet)
        self._trackSet = trackSet
        self._tags = {}
        self._trackNum = 0
        self._startTrackNum = 1
        self._progress = -1
        self._status  = Statuses.NotRunning
        self._resultFileNameCache = None
        self._resultFilePathCache = None
        self._codePage = QString()
        self._cueIndex0 = TrackIndex()
        self._cueIndex1 = TrackIndex()

        self.connect(settings, SIGNAL("changed()"), self._settingsChanged)
        self._settingsChanged()


   #****************************************************************
   #
   #****************************************************************
    def trackSet(self):
        return self._trackSet


   #****************************************************************
   #
   #****************************************************************
    def index(self):
        return self._trackSet.indexOf(self)


   #****************************************************************
   #
   #****************************************************************
    def emitDataChanged(self):
        self._trackSet.trackDataChanged(self)


   #****************************************************************
   # This return non-Unicode python string
   #****************************************************************
    def dataForAutoDetect(self):
        ret = ""
        for tag in ("TITLE", "PERFORMER"):
            try:
                (value, encoded) = self._tags[tag]
                if not encoded:
                    if isinstance(value, QString):
                        ret += value.toAscii().data()
                    else:
                        ret += value

            except KeyError, e:
                pass

        return ret


   #****************************************************************
   #
   #****************************************************************
    def tag(self, tagName):
        try:
            (value, encoded) = self._tags[tagName]
            if encoded:
                return value
            else:
                return self._trackSet.decode(value)

        except KeyError, e:
            return QString()


   #****************************************************************
   #
   #****************************************************************
    def setTag(self, tagName, value, encoded=True):
        self._resultFileNameCache = None
        self._resultFilePathCache = None
        self._tags[tagName] = (value, encoded)
        self.emitDataChanged()


   #****************************************************************
   #
   #****************************************************************
    def artist(self):
        return self.tag('PERFORMER')


   #****************************************************************
   #
   #****************************************************************
    def setArtist(self, value, encoded=True):
        self.setTag('PERFORMER', value, encoded)


   #****************************************************************
   #
   #****************************************************************
    def album(self):
        return self.tag('ALBUM')


   #****************************************************************
   #
   #****************************************************************
    def setAlbum(self, value, encoded=True):
        self.setTag('ALBUM', value, encoded)


   #****************************************************************
   #
   #****************************************************************
    def comment(self):
        return self.tag('COMMENT')


   #****************************************************************
   #
   #****************************************************************
    def setComment(self, value, encoded=True):
        self.setTag('COMMENT', value, encoded)


   #****************************************************************
   #
   #****************************************************************
    def title(self):
        return self.tag('TITLE')


   #****************************************************************
   #
   #****************************************************************
    def setTitle(self, value, encoded=True):
        self.setTag('TITLE', value, encoded)


   #****************************************************************
   #
   #****************************************************************
    def year(self):
        res = self.tag('YEAR')
        if res:
            return int(self.tag('YEAR'))
        else:
            return 1900


   #****************************************************************
   #
   #****************************************************************
    def setYear(self, value):
        try:
            self.setTag('YEAR', int(value), True)
        except ValueError:
            # Try YYYY.MM.DD format
            m = re.search('(\d\d\d\d).\d\d.\d\d', value)
            if m:
                self.setTag('YEAR', int(m.group(1)), True)
                return

            # Try DD.MM.YYYY format
            m = re.search('\d\d.\d\d.(\d\d\d\d)', value)
            if m:
                self.setTag('YEAR', int(m.group(1)), True)
                return


   #****************************************************************
   #
   #****************************************************************
    def genre(self):
        return self.tag('GENRE')


   #****************************************************************
   #
   #****************************************************************
    def setGenre(self, value, encoded=True):
        self.setTag('GENRE', value, encoded)


   #****************************************************************
   #
   #****************************************************************
    def flags(self):
        return self.tag('FLAGS')


   #****************************************************************
   #
   #****************************************************************
    def setFlags(self, value, encoded=True):
        self.setTag('FLAGS', value, encoded)


   #****************************************************************
   #
   #****************************************************************
    def isrc(self):
        return self.tag('ISRC')


   #****************************************************************
   #
   #****************************************************************
    def setIsrc(self, value, encoded=True):
        self.setTag('ISRC', value, encoded)


   #****************************************************************
   #
   #****************************************************************
    def songWriter(self):
        return self.tag('SONGWRITER')


   #****************************************************************
   #
   #****************************************************************
    def setSongWriter(self, value, encoded=True):
        self.setTag('SONGWRITER', value, encoded)


   #****************************************************************
   #
   #****************************************************************
    def trackNum(self):
        return int(self.index()) + int(self._startTrackNum)


   #****************************************************************
   #
   #****************************************************************
    def setTrackNum(self, value):
        self._resultFileNameCache = None
        self._resultFilePathCache = None
        self._trackNum = value
        self.emitDataChanged()


   #****************************************************************
   #
   #****************************************************************
    def startTrackNum(self):
        return self._startTrackNum


   #****************************************************************
   #
   #****************************************************************
    def setStartTrackNum(self, value):
        self._startTrackNum = value
        self.emitDataChanged()


   #****************************************************************
   #
   #****************************************************************
    def cueIndex0(self):
        return self._cueIndex0


   #****************************************************************
   #
   #****************************************************************
    def setCueIndex0(self, cueIndex):
        self._cueIndex0 = cueIndex


   #****************************************************************
   #
   #****************************************************************
    def cueIndex1(self):
        return self._cueIndex1


   #****************************************************************
   #
   #****************************************************************
    def setCueIndex1(self, cueIndex):
        self._cueIndex1 = cueIndex


   #****************************************************************
   #
   #****************************************************************
    def _settingsChanged(self):
        self._resultFileNameCache = None
        self._resultFilePathCache = None
        self._pattern = settings.value('OutFiles/Pattern').toString()
        self._outFormat = settings.value('OutFiles/Format').toString()


   #****************************************************************
   #
   #****************************************************************
    @staticmethod
    def safeString(str):
        res = str[:]
        res = res.replace('|', "-")
        res = res.replace('/', "-")
        res = res.replace('\\', "-")
        res = res.replace(':', "-")
        res = res.replace('*', "-")
        res = res.replace('?', "")
        return res


    def expandPattern(self, pattern, tokens, optional):
        res = ''
        perc = False
        hasVars = False
        isValid = True


        i = 0
        while i < len(pattern):
            c = str(pattern[i:i+1])

            # Sub pattern .................................
            if c == '{':
                n=0
                start = i + 1
                j = i
                s = "{"
                while j < len(pattern):
                    c = str(pattern[j:j+1])
                    if c == '{':
                        n+=1
                    elif c == '}':
                        n-=1

                    if n == 0:
                        s =  self.expandPattern(pattern[start:j], tokens, True)
                        i = j
                        break

                    j+=1
                res += s
            # Sub pattern .................................

            else:
                if (perc):
                    perc = False
                    try:
                        s = tokens[c]
                        hasVars = True
                        isValid = len(s) > 0
                        res += s
                    except KeyError:

                        if   (c == '%'):
                            res += '%';
                        else:
                            res += '%' + c

                else:
                    if (c == '%'):
                        perc = True

                    else:
                        res += c

            i+=1


        if perc:
            res += '%'

        if optional:
            if  hasVars:
                if not isValid:
                    return ''
            else:
                return "{%s}" % res

        return res


   #****************************************************************
   # %N  Number of tracks       %n  Track number
   # %a  Artist                 %A  Album title
   # %y  Year                   %g  Genre
   # %t  Track title
   #****************************************************************
    def resultFileName(self):
        if (self._resultFileNameCache and
            self._codePage == self._trackSet.realCodePage()):
            return self._resultFileNameCache

        pattern = self._pattern

        if (not pattern):
            pattern = QString("%a/%y - %A/%n - %t")


        tokens = {
            'N': "%02d" % int(self._trackSet.count()),
            'n': "%02d" % int(self.trackNum()),
            'A': Track.safeString(self.album()),
            't': Track.safeString(self.title()),
            'a': Track.safeString(self.artist()),
            'g': Track.safeString(self.genre()),
            'y': "%02d" % int(self.year()) if (self.year()) else ""
        }

        res = self.expandPattern(pattern, tokens, False)

        ext = outputAudioFormats.current().ext
        self._codePage == self._trackSet.realCodePage()
        self._resultFileNameCache = QString("%1.%2").arg(res).arg(ext)
        return self._resultFileNameCache


   #****************************************************************
   #
   #****************************************************************
    def resultFilePath(self):
        if (self._resultFilePathCache and
            self._codePage == self._trackSet.realCodePage()):
            return self._resultFilePathCache

        fileName = self.resultFileName()

        if fileName == '':
            return QString('')

        res = QFileInfo(fileName)

        if (res.isAbsolute()):
            return res.absoluteFilePath()

        # Add dir from trackList.outDir ===================
        outDir = settings.value('OutFiles/Directory').toString()
        if not outDir.isEmpty():
            if outDir.startsWith("~/"):
                outDir.replace(0, 1, QDir.homePath())

            res = QFileInfo(outDir + "/" + res.filePath())

        if (res.isAbsolute()):
            return res.absoluteFilePath()

        # Add dir from trackList.audioFile file ===========
        audioFileName = self._trackSet.disc().audioFileName()
        af = QFileInfo(audioFileName).dir().absolutePath()

        if (not af.isEmpty()):
            res = QFileInfo(af + "/" + res.filePath())

        self._codePage == self._trackSet.realCodePage()
        self._resultFilePathCache = res.absoluteFilePath()
        return self._resultFilePathCache


   #****************************************************************
   #
   #****************************************************************
    def discId(self):
        return self._trackSet.discId()


   #****************************************************************
   #
   #****************************************************************
    def progress(self):
        return self._progress


   #****************************************************************
   #
   #****************************************************************
    def status(self):
        return self._status


   #****************************************************************
   #
   #****************************************************************
    def setProgress(self, status, percent = -1):

        s2s = [# ------------ New statuses -----------
               # NoRun   Cancel Error  Abort   OK   Process |_____________
               [    0,     1,     1,     1,     1,     1    ], # NoRun    |
               [    1,     0,     0,     0,     0,     0    ], # Cancel   |
               [    1,     0,     0,     0,     0,     0    ], # Error    | Current
               [    1,     0,     1,     0,     0,     0    ], # Aborted  | statuses
               [    1,     0,     1,     0,     0,     0    ], # OK       |
               [    1,     0,     1,     1,     1,     1    ], # Process  |
               ]

        # Process is some is Extracting, Encoding, Queued  etc.
        procStat = len(s2s)-1

        if not s2s[min(procStat, self._status)][min(procStat, status)]:
            return

        self._status = status
        self._progress = percent



class PreGapTrack(Track):
   #****************************************************************
   #
   #****************************************************************
    def __init__(self, trackSet):
        Track.__init__(self, trackSet)
        ft = trackSet[0]
        self.setAlbum(ft.album())
        self.setArtist(ft.artist())
        self.setGenre(ft.genre())
        self.setTitle("(HTOA)")
        self.setTrackNum(1)
        self.setStartTrackNum(0)
        self.setYear(ft.year())
